#include <assert.h>
#include <vector>

#include "PMesh.h"
#include "IndexProcessor.h"
#include "PMeshManager.h"

MeshProcessing::PMesh::PMesh()
{
	initializeTriMesh();
}
MeshProcessing::PMesh::~PMesh()
{
	destroyMesh();
}

void MeshProcessing::PMesh::setVertices(uint numVertices, float* in)
{
	if (vertices) {
		delete[] vertices;
	}

	n_vertices = numVertices;

	uint vertexBufferLength = numVertices * 3;
	vertices = new float[vertexBufferLength];
	for (uint i = 0; i < vertexBufferLength; ++i) {
		vertices[i] = in[i];
	}

	m_bPreprocess = false;
}
void MeshProcessing::PMesh::setIndices(uint numIndices, uint* in)
{
	assert(numIndices % 3 == 0);

	if (triangles) {
		delete[] triangles;
	}

	n_triangles = numIndices / 3;
	triangles = new uint[numIndices];
	for (uint i = 0; i < numIndices; ++i) {
		triangles[i] = in[i];
	}

	m_bPreprocess = false;
}

void MeshProcessing::PMesh::preprocessMesh(bool bRemoveDegenerate)
{
	if (m_bPreprocess) return;
	m_bPreprocess = true;

	CorkTriMesh* out = new CorkTriMesh();
	IndexProcessor::simplifyMesh(this, out, bRemoveDegenerate);

	destroyMesh();

	n_triangles = out->n_triangles; out->n_triangles = 0;
	n_vertices = out->n_vertices; out->n_vertices = 0;
	vertices = out->vertices; out->vertices = NULL;
	triangles = out->triangles; out->triangles = NULL;

	delete out;
}

uint MeshProcessing::PMesh::getNumVertices()
{
	return n_vertices;
}
uint MeshProcessing::PMesh::getNumIndices()
{
	return n_triangles * 3;
}

uint* MeshProcessing::PMesh::getIndices()
{
	return triangles;
}
float* MeshProcessing::PMesh::getVertices()
{
	return vertices;
}

void MeshProcessing::PMesh::update()
{
	if (vertices) {
		delete[] vertices;
	}

	if (triangles) {
		delete[] triangles;
	}

	n_vertices = m_tempVertexData.size() / 3;
	n_triangles = m_tempIndexData.size() / 3;

	vertices = new float[m_tempVertexData.size()];
	triangles = new uint[m_tempIndexData.size()];

	for (uint i = 0; i < m_tempVertexData.size(); ++i) {
		vertices[i] = (float)m_tempVertexData[i];
	}

	for (uint i = 0; i < m_tempIndexData.size(); ++i) {
		triangles[i] = m_tempIndexData[i];
	}
	m_bPreprocess = false;
}

void MeshProcessing::PMesh::destroyMesh()
{
	n_triangles = 0;
	n_vertices = 0;
	delete[] triangles;
	delete[] vertices;
	m_tempIndexData.clear();
	m_tempVertexData.clear();
}

Handle MeshProcessing::PMesh::toHandle(){
	return (Handle)this;
}

void MeshProcessing::PMesh::initializeTriMesh()
{
	n_triangles = 0;
	n_vertices = 0;
	triangles = nullptr;
	vertices = nullptr;
}

#ifdef __EMSCRIPTEN__
emscripten::val MeshProcessing::PMesh::getIndexArray() {
	using namespace emscripten;

	val jsArray = val::array();

	for (size_t i = 0; i < this->n_triangles * 3; ++i) {
		jsArray.set(i, this->triangles[i]);
	}

	return jsArray;
}
emscripten::val MeshProcessing::PMesh::getVetexArray() {
	using namespace emscripten;

	val jsArray = val::array();

	for (size_t i = 0; i < this->n_vertices * 3; ++i) {
		jsArray.set(i, this->vertices[i]);
	}

	return jsArray;
}
void MeshProcessing::PMesh::addVertices(emscripten::val vertices_) {
	if (vertices) {
		delete[] vertices;
	}
	auto bufferLength = vertices_["length"].as<int>();
	n_vertices = bufferLength / 3;
	vertices = new float[bufferLength];

	for (uint i = 0; i < bufferLength; ++i) {
		vertices[i] = vertices_[i].as<float>();
	}

	m_bPreprocess = false;
}
void MeshProcessing::PMesh::addTriangles(emscripten::val indices) {
	if (triangles) {
		delete[] triangles;
	}
	auto bufferLength = indices["length"].as<int>();
	n_triangles = bufferLength / 3;
	triangles = new uint[bufferLength];

	for (uint i = 0; i < bufferLength; ++i) {
		triangles[i] = indices[i].as<uint>();
	}

	m_bPreprocess = false;
}
#else
void MeshProcessing::PMesh::addVertices(uint numVertices, float* in) {
	setVertices(numVertices, in);
}
void MeshProcessing::PMesh::addTriangles(uint numIndices, uint* in) {
	setIndices(numIndices, in);
}
#endif
